/**
 * @author phi
 * canvas を拡張
 */

(function(){ "use strict"; })();

/** @ignore */
var TM = TM || {};

/**
 * @namespace
 * @memberof    TM
 */
TM.Graphics = TM.Graphics || {};


(function()
{
    
    /**
     * @lends   TM.Graphics.Graphics.prototype
     */
    TM.Graphics.Graphics = TM.createClass(
    {
        /**
         * キャンバス
         */
        canvas: null,
        /**
         * コンテキスト
         */
        context: null,
        
        /** @constructs */
        init: function(canvas) {
            // デフォルト値設定
            this.canvas = canvas || TM.$create("canvas");
            this.context = this.canvas.getContext('2d');
            this.context.lineCap    = "round";
            this.context.lineJoin   = "round";
        },
        
        /**
         *  フルスクリーン化
         */
        fullScreen: function() {
            this.canvas.style.position = "fixed";
            this.canvas.style.margin   = "0";
            this.canvas.style.padding  = "0";
            this.canvas.style.left     = "0px";
            this.canvas.style.top      = "0px";
            this.canvas.width  = window.innerWidth;
            this.canvas.height = window.innerHeight;
            
            var self = this;
            window.addEventListener("resize", function(){
                self.canvas.width = window.innerWidth;
                self.canvas.height = window.innerHeight;
            });
        },
        
        /**
         * ウィンドウにフィットさせる
         */
        fitWindowSize: function() {
            with (this.canvas.style) {
                position = "fixed";
                margin   = "0";
                padding  = "0";
                left     = "0px";
                top      = "0px";
            }
            this.canvas.width  = window.innerWidth;
            this.canvas.height = window.innerHeight;
            
            var self = this;
            window.addEventListener("resize", function(){
                self.canvas.width = window.innerWidth;
                self.canvas.height = window.innerHeight;
            });
            
            return this;
        },
        
        /**
         *  クリア
         */
        clear: function(x, y, width, height)
        {
            x = x || 0;
            y = y || 0;
            width = width || this.width;
            height= height|| this.height;
            this.context.clearRect(x, y, width, height);
            return this;
        },
        
        /**
         * 色指定クリア
         */
        clearWithColor: function(fillStyle, x, y, width, height)
        {
            x = x || 0;
            y = y || 0;
            width = width || this.width;
            height= height|| this.height;
            
            this.save();
            this.resetTransform();          // 行列初期化
            this.fillStyle = fillStyle;     // 塗りつぶしスタイルセット
            this.context.fillRect(x, y, width, height);
            this.restore();
            
            return this;
        },
                
        /**
         *  パスを開始(リセット)
         */
        beginPath: function()
        {
            this.context.beginPath();
            return this;
        },
                
        /**
         *  パスを閉じる
         */
        closePath: function()
        {
            this.context.closePath();
            return this;
        },
        
        /**
         *  新規パス生成
         */
        moveTo: function(x, y)
        {
            this.context.moveTo(x, y);
            return this;
        },
        
        /**
         * パスに追加
         */
        lineTo: function(x, y)
        {
            this.context.lineTo(x, y);
            return this;
        },
        
        /**
         * パス内を塗りつぶす
         */
        fill: function()
        {
            this.context.fill();
            return this;
        },
        
        /**
         * パス上にラインを引く
         */
        stroke: function()
        {
            this.context.stroke();
            return this;
        },
        
        /**
         * 点描画
         */
        drawPoint: function(x, y)
        {
            return this.strokeRect(x, y, 1, 1);
            // return this.beginPath().moveTo(x-0.5, y-0.5).lineTo(x+0.5, y+0.5).stroke();
        },

        /**
         * ラインパスを作成
         */
        line: function(x0, y0, x1, y1)
        {
            return this.moveTo(x0, y0).lineTo(x1, y1);
        },
        
        drawLine: function(x0, y0, x1, y1)
        {
            return this.beginPath().line(x0, y0, x1, y1).stroke();
        },
        
        /**
         * 
         */
        drawDashLine: function(x0, y0, x1, y1, pattern)
        {
            var patternTable = null;
            if (typeof(pattern) == "string") {
                patternTable = pattern;
            }
            else {
                pattern = pattern || 0xf0f0;
                patternTable = pattern.toString(2);
            }
            patternTable = patternTable.padding(16, '1');
            
            var vx = x1-x0;
            var vy = y1-y0;
            var len = Math.sqrt(vx*vx + vy*vy);
            vx/=len; vy/=len;
            
            var x = x0;
            var y = y0;
            for (var i=0; i<len; ++i) {
                if (patternTable[i%16] == '1') {
                    this.drawPoint(x, y);
                    // this.fillRect(x, y, this.context.lineWidth, this.context.lineWidth);
                }
                x += vx;
                y += vy;
            }
            
            return this;
        },
        
        /**
         * v0(x0, y0), v1(x1, y1) から角度を求めて矢印を描画
         * http://hakuhin.jp/as/rotation.html
         */
        drawArrow: function(x0, y0, x1, y1, arrowRadius)
        {
            var vx = x1-x0;
            var vy = y1-y0;
            var angle = Math.atan2(vy, vx)*180/Math.PI;
            
            this.drawLine(x0, y0, x1, y1);
            this.fillPolygon(x1, y1, arrowRadius || 5, 3, angle);
            
            return this;
        },
        
        
        /**
         * lines
         */
        lines: function()
        {
            this.moveTo(arguments[0], arguments[1]);
            for (var i=1,len=arguments.length/2; i<len; ++i) {
                this.lineTo(arguments[i*2], arguments[i*2+1]);
            }
            return this;
        },
        
        strokeLines: function()
        {
            this.beginPath();
            this.lines.apply(this, arguments);
            this.stroke();
            return this;
        },
        
        fillLines: function()
        {
            this.beginPath();
            this.lines.apply(this, arguments);
            this.fill();
            return this;
        },
        
        /**
         * 四角形パスを作成する
         */
        rect: function(x, y, width, height)
        {
            this.context.rect.apply(this.context, arguments);
            return this;
        },
        
        /**
         * 四角形塗りつぶし描画
         */
        fillRect: function()
        {
            this.context.fillRect.apply(this.context, arguments);
            return this;
        },
        
        /**
         * 四角形ライン描画
         */
        strokeRect: function()
        {
            this.context.strokeRect.apply(this.context, arguments);
            return this;
        },
        
        /**
         * 角丸四角形パス
         */
        roundRect: function(x, y, width, height, radius) {
            var l = x + radius;
            var r = x + width - radius;
            var t = y + radius;
            var b = y + height - radius;
            
            /*
            var ctx = this.context;
            ctx.moveTo(l, y);
            ctx.lineTo(r, y);
            ctx.quadraticCurveTo(x+width, y, x+width, t);
            ctx.lineTo(x+width, b);
            ctx.quadraticCurveTo(x+width, y+height, r, y+height);
            ctx.lineTo(l, y+height);
            ctx.quadraticCurveTo(x, y+height, x, b);
            ctx.lineTo(x, t);
            ctx.quadraticCurveTo(x, y, l, y);
            /**/
            
            this.context.arc(l, t, radius,     -Math.PI, -Math.PI*0.5, false);  // 左上
            this.context.arc(r, t, radius, -Math.PI*0.5,            0, false);  // 右上
            this.context.arc(r, b, radius,            0,  Math.PI*0.5, false);  // 右下
            this.context.arc(l, b, radius,  Math.PI*0.5,      Math.PI, false);  // 左下
            this.closePath();
            /**/
            
            return this;
        },
        /**
         * 角丸四角形塗りつぶし
         */
        fillRoundRect: function(x, y, width, height, radius) {
            return this.beginPath().roundRect(x, y, width, height, radius).fill();
        },
        /**
         * 角丸四角形ストローク描画
         */
        strokeRoundRect: function(x, y, width, height, radius) {
            return this.beginPath().roundRect(x, y, width, height, radius).stroke();
        },
        
        /**
         * ポリゴンパス
         */
        polygon: function(x, y, size, sides, offsetAngle) {
            var radDiv = (Math.PI*2)/sides;
            var radOffset = (offsetAngle!=undefined) ? offsetAngle*Math.PI/180 : -Math.PI/2;
            
            this.moveTo(x + Math.cos(radOffset)*size, y + Math.sin(radOffset)*size);
            for (var i=1; i<sides; ++i) {
                var rad = radDiv*i+radOffset;
                this.lineTo(
                    x + Math.cos(rad)*size,
                    y + Math.sin(rad)*size
                );
            }
            this.closePath();
            return this;
        },
        /**
         * ポリゴン塗りつぶし
         */
        fillPolygon: function(x, y, radius, sides, offsetAngle) {
            return this.beginPath().polygon(x, y, radius, sides, offsetAngle).fill();
        },
        /**
         * ポリゴンストローク描画
         */
        strokePolygon: function(x, y, radius, sides, offsetAngle) {
            return this.beginPath().polygon(x, y, radius, sides, offsetAngle).stroke();
        },
        
        /**
         * star
         */
        star: function(x, y, radius, sides, sideIndent, offsetAngle) {
            var sideIndentRadius = radius * (sideIndent || 0.38);
            var radOffset = (offsetAngle) ? offsetAngle*Math.PI/180 : -Math.PI/2;
            var radDiv = (Math.PI*2)/sides/2;
            
            this.moveTo(
                x + Math.cos(radOffset)*radius,
                y + Math.sin(radOffset)*radius
            );
            for (var i=1; i<sides*2; ++i) {
                var rad = radDiv*i + radOffset;
                var len = (i%2) ? sideIndentRadius : radius;
                this.lineTo(
                    x + Math.cos(rad)*len,
                    y + Math.sin(rad)*len
                );
            }
            this.closePath();
            return this;
        },
        
        fillStar: function(x, y, radius, sides, sideIndent, offsetAngle) {
            return this.beginPath().star(x, y, radius, sides, sideIndent, offsetAngle).fill();
        },
        strokeStar: function(x, y, radius, sides, sideIndent, offsetAngle) {
            return this.beginPath().star(x, y, radius, sides, sideIndent, offsetAngle).stroke();
        },
        
        
        /**
         * 
         */
        circle: function(x, y, radius)
        {
            this.context.arc(x, y, radius, 0, Math.PI*2, false);
            return this;
        },
        
        /**
         * 
         */
        fillCircle: function(x, y, radius)
        {
            return this.beginPath().circle(x, y, radius).fill();
        },
        
        /**
         * 
         */
        strokeCircle: function(x, y, radius)
        {
            return this.beginPath().circle(x, y, radius).stroke();
        },
        
        /**
         * 
         */
        triangle: function(x0, y0, x1, y1, x2, y2)
        {
            this.moveTo(x0, y0).lineTo(x1, y1).lineTo(x2, y2);
            this.closePath();
            return this;
        },
        
        /**
         * 
         */
        fillTriangle: function(x0, y0, x1, y1, x2, y2)
        {
            return this.beginPath().triangle(x0, y0, x1, y1, x2, y2).fill();
        },
        
        /**
         * 
         */
        strokeTriangle: function(x0, y0, x1, y1, x2, y2)
        {
            return this.beginPath().triangle(x0, y0, x1, y1, x2, y2).stroke();
        },
        

        /**
         * 
         */
        fillText: function(text, x, y)
        {
            return this.context.fillText.apply(this.context, arguments);
        },
        
        /**
         * 
         */
        strokeText: function(text, x, y)
        {
            return this.context.strokeText.apply(this.context, arguments);
        },
        
        /**
         * 
         */
        fillTextList: function(text_list, x, y, offsetX, offsetY)
        {
            offsetX = offsetX || 0;
            offsetY = offsetY || 20;
            
            for (var i=0,len=text_list.length; i<len; ++i) {
                this.fillText(text_list[i], x+offsetX*i, y+offsetY*i);
            }
            
            return this;
        },
        
        /**
         * 
         */
        strokeTextList: function(text_list, x, y, offsetX, offsetY)
        {
            offsetX = offsetX || 0;
            offsetY = offsetY || 20;
            
            for (var i=0,len=text_list.length; i<len; ++i) {
                this.strokeText(x+offsetX*i, y+offsetY*i, text_list[i]);
            }
            
            return this;
        },
                
        /**
         * 
         */
        drawImage: function(image, x, y)
        {
            this.context.drawImage.apply(this.context, arguments);
            return ;
            
            x = x || 0;
            y = y || 0;
            this.context.drawImage(image, x, y);
            return this;
            // ctx.drawImage(this.image.canvas,
                // 0, 0, this.width, this.height,
                // -this.width/2, -this.height/2, this.width, this.height);
        },
        
        /**
         * 
         */
        setTransform: function(m11, m12, m21, m22, dx, dy)
        {
            this.context.setTransform(m11, m12, m21, m22, dx, dy);
            return this;
        },
        
        
        /**
         * 
         */
        resetTransform: function()
        {
            this.setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
            return this;
        },
        
        
        /**
         * 
         */
        setTransformCenter: function()
        {
            this.context.setTransform(1, 0, 0, 1, this.width/2, this.height/2);
            return this;
        },
        
        /**
         * 
         */
        save: function()
        {
            this.context.save();
            return this;
        },
        
        /**
         * 
         */
        restore: function()
        {
            this.context.restore();
            return this;
        },
        
        /**
         * 
         */
        translate: function(x, y)
        {
            this.context.translate(x, y);
            return this;
        },
        
        /**
         * 
         */
        rotate: function(rotation)
        {
            this.context.rotate(rotation);
            return this;
        },
        
        /**
         * 
         */
        scale: function(scaleX, scaleY)
        {
            this.context.scale(scaleX, scaleY);
            return this;
        },
        
        /**
         */
        saveAsImage: function(mime_type) {
            mime_type = mime_type || Graphics.MIME_TYPE_PNG;
            var data_url = this.canvas.toDataURL(mime_type);
            // data_url = data_url.replace(mime_type, "image/octet-stream");
            window.open(data_url, "save");
            
            // toDataURL を使えば下記のようなツールが作れるかも!!
            // TODO: プログラムで絵をかいて保存できるツール
        },
        
        setCompositing: function(alpha, compositeOperation)
        {
            // TODO
        },
        
        /**
         * 
         * @see <a href="http://www.w3.org/TR/2010/WD-2dcontext-20100624/#colors-and-styles">http://www.w3.org/TR/2010/WD-2dcontext-20100624/#colors-and-styles</a>
         */
        setColorStyle: function(stroke, fill)
        {
            fill = fill || stroke;
            
            this.context.strokeStyle    = stroke;
            this.context.fillStyle      = fill;
            return this;
        },
        
        /**
         * 
         */
        setText: function(font, align, baseline)
        {
            var c = this.context;
            c.font          = font;
            c.textAlign     = align;
            c.textBaseline  = baseline;
        },
        
        /**
         * ラインスタイルを一括セット
         * @see <a href="http://www.w3.org/TR/2010/WD-2dcontext-20100624/#line-styles">http://www.w3.org/TR/2010/WD-2dcontext-20100624/#line-styles</a>
         */
        setLineStyle: function(width, cap, join, miter) {
            with(this.context) {
                lineWidth   = width || 1;
                lineCap     = cap   || "round";
                lineJoin    = join  || "round";
                miterLimit  = miter || 10.0;
            }
            return this;
        },
        
        /**
         * 
         * @see <ul>
         *          <li><a href="http://www.html5.jp/canvas/ref/property/shadowColor.html">http://www.html5.jp/canvas/ref/property/shadowColor.html</a></li>
         *          <li><a href="http://www.w3.org/TR/2010/WD-2dcontext-20100624/#shadows">http://www.w3.org/TR/2010/WD-2dcontext-20100624/#shadows</a></li>
         *      </ul>
         */
        setShadows: function(color, offsetX, offsetY, blur) {
            with(this.context) {
                shadowColor     = color     || "black";
                shadowOffsetX   = offsetX   || 0;
                shadowOffsetY   = offsetY   || 0;
                shadowBlur      = blur      || 0;
            }
            return this;
        }
    });
    
    var Graphics = TM.Graphics.Graphics;
    
    Graphics.MIME_TYPE_PNG = "image/png";
    Graphics.MIME_TYPE_JPG = "image/jpeg";
    Graphics.MIME_TYPE_SVG = "image/svg+xml";
    
    /**
     * 幅
     * @name        width
     * @fieldof     TM.Graphics.Graphics.prototype
     */
    Graphics.prototype.accessor("width", {
        "get": function()   { return this.canvas.width; },
        "set": function(v)  { this.canvas.width = v; }
    });
    
    /**
     * 高さ
     * @name        height
     * @fieldof     TM.Graphics.Graphics.prototype
     */
    Graphics.prototype.accessor("height", {
        "get": function()   { return this.canvas.height; },
        "set": function(v)  { this.canvas.height = v;   }
    });
    
    /**
     * 塗りつぶしスタイル
     * @name        fillStyle
     * @fieldof     TM.Graphics.Graphics.prototype
     */
    Graphics.prototype.accessor("fillStyle", {
        "get": function()   { return this.context.fillStyle; },
        "set": function(v)  { this.context.fillStyle = v;   }
    });
    
    
    /**
     * ストロークスタイル
     * @name        strokeStyle
     * @fieldof     TM.Graphics.Graphics.prototype
     */
    Graphics.prototype.accessor("strokeStyle", {
        "get": function()   { return this.context.strokeStyle; },
        "set": function(v)  { this.context.strokeStyle = v;   }
    });
    
    
    /**
     * アルファ指定
     * @name        globalAlpha
     * @fieldof     TM.Graphics.Graphics.prototype
     */
    Graphics.prototype.accessor("globalAlpha", {
        "get": function()   { return this.context.globalAlpha; },
        "set": function(v)  { this.context.globalAlpha = v;   }
    });
    
    
    /**
     * ブレンド指定
     * @name        globalCompositeOperation
     * @fieldof     TM.Graphics.Graphics.prototype
     */
    Graphics.prototype.accessor("globalCompositeOperation", {
        "get": function()   { return this.context.globalCompositeOperation; },
        "set": function(v)  { this.context.globalCompositeOperation = v;   }
    });

    /**
     * シャドウブラー
     * @name        shadowBlur
     * @fieldof     TM.Graphics.Graphics.prototype
     */
    Graphics.prototype.accessor("shadowBlur", {
        "get": function()   { return this.context.shadowBlur; },
        "set": function(v)  { this.context.shadowBlur = v;   }
    });
    

    /**
     * シャドウブラーカラー
     * @name        shadowColor
     * @fieldof     TM.Graphics.Graphics.prototype
     */
    Graphics.prototype.accessor("shadowColor", {
        "get": function()   { return this.context.shadowColor; },
        "set": function(v)  { this.context.shadowColor = v;   }
    });
    
    /**
     * ライン終端の描画方法
     * @name        lineCap
     * @fieldof     TM.Graphics.Graphics.prototype
     */
    Graphics.prototype.accessor("lineCap", {
        "get": function()   { return this.context.lineCap; },
        "set": function(v)  { this.context.lineCap = v;   }
    });
    
    /**
     * ラインつなぎ目の描画方法
     * @name        lineJoin
     * @fieldof     TM.Graphics.Graphics.prototype
     */
    Graphics.prototype.accessor("lineJoin", {
        "get": function()   { return this.context.lineJoin; },
        "set": function(v)  { this.context.lineJoin = v;   }
    });
    
    /**
     * ライン幅設定
     * @name        lineWidth
     * @fieldof     TM.Graphics.Graphics.prototype
     */
    Graphics.prototype.accessor("lineWidth", {
        "get": function()   { return this.context.lineWidth; },
        "set": function(v)  { this.context.lineWidth = v;   }
    });
    
    /**
     * フォント
     * @name        font
     * @fieldof     TM.Graphics.Graphics.prototype
     */
    Graphics.prototype.accessor("font", {
        "get": function()   { return this.context.font; },
        "set": function(v)  { this.context.font = v;   }
    });
    

    /**
     * 
     * @name        centerX
     * @fieldof     TM.Graphics.Graphics.prototype
     */
    Graphics.prototype.getter("centerX", function(){
        return this.canvas.width/2;
    });
    
    /**
     * 
     * @name        centerY
     * @fieldof     TM.Graphics.Graphics.prototype
     */
    Graphics.prototype.getter("centerY", function(){
        return this.canvas.height/2;
    });
    
    
    
    
    console.assert(!TM.$Graphics, "名前が競合しています!!", "$Graphics");
    /**
     * TM.Graphics.Graphics の alias.
     * short hand としてお使いください.
     */
    TM.$Graphics = TM.Graphics.Graphics;
    
})();

/*
 * imageData
 */
(function(){
    
    var Graphics = tm.Graphics.Graphics;
    
    var ImageData = tm.createClass({
        
        graphics: null,
        imageData: null,
        
        init: function(imageData) {
            this.imageData = imageData;
        },
        
        getPixel: function(index) {
            var i = index*4;
            return [
                this.imageData.data[i+0],
                this.imageData.data[i+1],
                this.imageData.data[i+2],
                this.imageData.data[i+3]
            ];
        },
        
        getPixelAsNumber: function(index) {
            var i = index*4;
            return (this.data[i+3] << 24) | (this.data[i+0] << 16) | (this.data[i+1] << 8) | this.data[i+2];
        },
        
        getPixelAsObject: function(index) {
            var i = index*4;
            return {
                r: this.data[i+0],
                g: this.data[i+1],
                b: this.data[i+2],
                a: this.data[i+3]
            };
        },
        
        getPixelAsArray: function(index) {
            var i = index*4;
            return [
                this.imageData.data[i+0],
                this.imageData.data[i+1],
                this.imageData.data[i+2],
                this.imageData.data[i+3]
            ];
        },
        
        setPixel: function(index, r, g, b)
        {
            var i = index*4;
            this.data[i+0] = r;
            this.data[i+1] = g;
            this.data[i+2] = b;
            return this;
        },
        
        setPixel32: function(index, r, g, b, a)
        {
            var i = index*4;
            this.data[i+0] = r;
            this.data[i+1] = g;
            this.data[i+2] = b;
            this.data[i+3] = a;
            return this;
        },
        
        setPixelFromArray: function(index, pixel)
        {
            return this.setPixel(index, pixel[0], pixel[1], pixel[2]);
        },
        
        setPixel32FromArray: function(index, pixel)
        {
            return this.setPixel32(index, pixel[0], pixel[1], pixel[2], pixel[3]);
        },

        /**
         * argb
         */
        setPixelFromNumber: function(index, pixel)
        {
            return this.setPixel(index, (pixel & 0x00ff0000)>>>16, (pixel & 0x0000ff00)>>>8, (pixel & 0x000000ff)>>>0);
        },

        /**
         * argb
         */
        setPixel32FromNumber: function(index, pixel)
        {
            return this.setPixel32(index, (pixel & 0x00ff0000)>>>16, (pixel & 0x0000ff00)>>>8, (pixel & 0x000000ff)>>>0, (pixel & 0xff000000)>>>24);
        },
        
        /**
         * object
         */
        setPixelFromObject: function(index, pixel)
        {
            return this.setPixel(pixel.r, pixel.g, pixel.b);
        },
        setPixel32FromObject: function(index, pixel)
        {
            return this.setPixel32(pixel.r, pixel.g, pixel.b, pixel.a);
        },
        
        /**
         * string
         * rgb, hsl, #... #...... などに対応予定
         */
        setPixelFromString: function(index, pixel)
        {
            
        },
        
        posToIndex: function(x, y) {
            return y*this.width + x;
        },
        
        

        // filter: function(rect, filter)
        filter: function(filter)
        {
            for (var i=0; i<this.height; ++i) {
                for (var j=0; j<this.width; ++j) {
                    var index = this.posToIndex(j, i);
                    var p = this.getPixel(index);
                    
                    filter.calc(p, index, j, i, this);
                }
            }
            
            return this;
        },

        noise: function(low, high)
        {
            low = low  || 0;
            high= high || 255;
            range= high-low;
            this.filter({
                calc: function(p, index, x, y, imageData) {
                    p[0] = Math.random()*range + low;
                    p[1] = Math.random()*range + low;
                    p[2] = Math.random()*range + low;
                    imageData.setPixelFromArray(index, p);
                }
            })
        }
        
    });
    
    
    ImageData.prototype.accessor("width", {
        "get": function()   { return this.imageData.width; },
        "set": function(v)  { this.iamgeData.width = v;    }
    });
    
    ImageData.prototype.accessor("height", {
        "get": function()   { return this.imageData.height; },
        "set": function(v)  { this.iamgeData.height = v;    }
    });
    
    ImageData.prototype.accessor("data", {
        "get": function()   { return this.imageData.data; },
        "set": function(v)  { this.iamgeData.data = v;    }
    });
    
    Graphics.prototype.getImageData = function(x, y, width, height)
    {
        // new ImageData();
        return ImageData(this.context.getImageData(x||0, y||0, width||this.width, height||this.height));
    };
    
    Graphics.prototype.createImageData = function(width, height)
    {
        // new ImageData();
        return ImageData(this.context.createImageData(width||this.width, height||this.height));
    };
    
    Graphics.prototype.putImageData = function(imageData)
    {
        this.context.putImageData(imageData.imageData, 0, 0);
    };

})();


/*
 * filter
 */
(function(){
    
    tm.Graphics.ReverseFilter = {
        calc: function(p, index, x, y, imageData) {
            p[0] = 255-p[0];
            p[1] = 255-p[1];
            p[2] = 255-p[2];
            imageData.setPixelFromArray(index, p);
        }
    };
    
    tm.Graphics.MonochromeFilter = {
        calc: function(p, index, x, y, imageData) {
            var grayscale = p[0]*0.3 + p[1]*0.59 + p[2]*0.11;
            p[0] = grayscale;
            p[1] = grayscale;
            p[2] = grayscale;
            imageData.setPixelFromArray(index, p);
        }
    };
    
    tm.Graphics.RedFilter = {
        calc: function(p, index, x, y, imageData) {
            p[0]=p[0]*2;
            if (p[0] > 255) p[0] = 255;
            p[1] = Math.floor(p[1]/2);
            p[2] = Math.floor(p[2]/2);
            imageData.setPixelFromArray(index, p);
        }
    };
    
    tm.Graphics.GreenFilter = {
        calc: function(p, index, x, y, imageData) {
            p[1]=p[1]*2;
            if (p[1] > 255) p[1] = 255;
            p[0] = Math.floor(p[0]/2);
            p[2] = Math.floor(p[2]/2);
            imageData.setPixelFromArray(index, p);
        }
    };
    
    tm.Graphics.BlueFilter = {
        calc: function(p, index, x, y, imageData) {
            p[2]=p[2]*2;
            if (p[2] > 255) p[2] = 255;
            p[0] = Math.floor(p[0]/2);
            p[1] = Math.floor(p[1]/2);
            imageData.setPixelFromArray(index, p);
        }
    };
    
    tm.Graphics.ColorTransformFilter = {
        redMult     : 1.0,
        greenMult   : 1.0,
        blueMult    : 1.0,
        alphaMult   : 1.0,
        
        redOffset   : 0.0,
        greenOffset : 0.0,
        blueOffset  : 0.0,
        alphaOffset : 0.0,
        calc: function(p, index, x, y, imageData) {
            p[0] = p[0]*this.redMult   + this.redOffset;
            p[1] = p[1]*this.greenMult + this.greenOffset;
            p[2] = p[2]*this.blueMult  + this.blueOffset;
            p[3] = p[3]*this.alphaMult + this.alphaOffset;
            imageData.setPixelFromArray(index, p);
        }
    };
    
    return ;
    var Canvas = TM.DOM.Canvas;

    var Filter = TM.createClass({
        
        dom: null,
        
        init: function(dom) {
            this.dom = dom;
        },
        
        filter: function(fn, x, y, width, height)
        {
            x       = x || 0;
            y       = y || 0;
            width   = width || this.dom.element.width;
            height  = height|| this.dom.element.height;
            
            var imageData = this.dom.imageData;
            imageData.setup(x, y, width, height);
            
            for (var i=0; i<width; ++i) {
                for (var j=0; j<height; ++j) {
                    var index = j*width + i;
                    var p = imageData.getPixel(index);
                    p = fn(p);
                    imageData.setPixel(index, p);
                }
            }
            
            this.dom.context.putImageData(imageData.imageData, x, y);
        },
        
        reverse: function(x, y, width, height)
        {
            this.filter(function(p){
                p[0] = 255-p[0];
                p[1] = 255-p[1];
                p[2] = 255-p[2];
                
                return p;
            }, x, y, width, height);
        },
        
        monochrome: function(x, y, width, height)
        {
            this.filter(function(p){
                var grayscale = p[0]*0.3 + p[1]*0.59 + p[2]*0.11;
                p[0] = grayscale;
                p[1] = grayscale;
                p[2] = grayscale;
                
                return p;
            }, x, y, width, height);
        },
        
        red: function(x, y, width, height)
        {
            this.filter(function(p){
                p[0]=p[0]*2;
                if (p[0] > 255) p[0] = 255;
                p[1] = Math.floor(p[1]/2);
                p[2] = Math.floor(p[2]/2);
                
                return p;
            }, x, y, width, height);
        },
        
    });
    
    
    Canvas.prototype.getter("filter", function(){
        return this._filter || ( this._filter = Filter(this) );
    });
    
})();



